package gld;

import static robocode.util.Utils.normalRelativeAngle;

import java.awt.Color;
import java.util.HashMap;
import java.util.Map;

import robocode.AdvancedRobot;
import robocode.RobotDeathEvent;
import robocode.ScannedRobotEvent;

public class HK47 extends AdvancedRobot {

	private Map<String, RobotModel> radarMap = new HashMap<String, RobotModel>();
	private static Map<String,int[]> targetStatsMap = new HashMap<String, int[]>();
	int direction = 1;
	private Calculation calc = new Calculation();
	private double gunTurn;
	private double bodyTurn;
	private double radarTurn;
	private int turn = 0;
	private int lastAttackTurn = 0;
	private boolean lookForTarget;

	@Override
	public void run() {
		setBodyColor(Color.RED);
		setGunColor(Color.WHITE);
		setRadarColor(Color.WHITE);
		setScanColor(Color.RED);
		setBulletColor(Color.RED);

		setAdjustGunForRobotTurn(true);
		setAdjustRadarForGunTurn(true);
		bodyTurn = 10;
		while(true) {
			if (lookForTarget) {
				radarTurn = bodyTurn;
				setTurnRightRadians(bodyTurn);
				setTurnRadarRightRadians(radarTurn);
				execute();
				if (turn - lastAttackTurn > 2) {
					bodyTurn = Math.toRadians(-10);
				}
				if (turn - lastAttackTurn > 5) {
					bodyTurn = Math.toRadians(10);
				}
			}
			lookForTarget = !attack();
			turn++;
		}
	}

	private boolean attack() {
		boolean attacked = false;
		RobotModel target = null;
		for (RobotModel scanned : radarMap.values()) {
			if (turn - scanned.getLastTurnScanned() < 5) {
				target = scanned;
			}
		}

		if (target != null) {
			attacked = true;
			lastAttackTurn = turn;
			
			double absBearing = calc.targetAbsoluteBearing(target.getBearingRadians(), getHeadingRadians());
			double ex = calc.targetLocationX(absBearing, target.getDistance(), getX());
			double ey = calc.targetLocationY(absBearing, target.getDistance(), getY());

			for (int i=0; i < target.waves.size(); i++)
			{
				WaveBullet currentWave = (WaveBullet)target.waves.get(i);
				if (currentWave.checkHit(ex, ey, getTime()))
				{
					target.waves.remove(currentWave);
					i--;
				}
			}
			
			bodyTurn = getHeadOnTargetTurn(target);
			radarTurn = bodyTurn;
			if (target.getDistance() > 150) {
				setTurnGunRightRadians(gunTurn);
				setTurnRightRadians(bodyTurn);
				setAhead(target.getDistance() - 140);
			} else if (target.getDistance() < 100) {
				if (target.getBearing() > -90 && target.getBearing() <= 90) {
					setBack(50);
				} else {
					setAhead(30);
				}
			} else {
				setTurnGunRightRadians(gunTurn);
			}
			setFireGuessFactor(target);
			setTurnRadarRightRadians(bodyTurn);
			execute();
		}
		return attacked;
	}

	private void setFireGuessFactor(RobotModel target) {
		int[] stats = targetStatsMap.get(target.name);
		double power = target.distance > 150 ? 1.5 : 3.0;
		double absBearing = calc.targetAbsoluteBearing(target.getBearingRadians(), getHeadingRadians());
		if (target.getVelocity() != 0) {
			if (Math.sin(target.getHeadingRadians()-absBearing)*target.getVelocity() < 0)
				direction = -1;
			else
				direction = 1;
		}
		WaveBullet newWave = new WaveBullet(getX(), getY(), absBearing, power,
                        direction, getTime(), stats);
		target.waves.add(newWave);
		
		int bestindex = 15;	// initialize it to be in the middle, guessfactor 0.
		for (int i=0; i<31; i++)
			if (stats[bestindex] < stats[i])
				bestindex = i;
 
		// this should do the opposite of the math in the WaveBullet:
		double guessfactor = (double)(bestindex - (stats.length - 1) / 2) / ((stats.length - 1) / 2);
		double angleOffset = direction * guessfactor * calc.maxEscapeAngle(3);
        gunTurn = normalRelativeAngle(absBearing - getGunHeadingRadians() + angleOffset);
        setTurnGunRightRadians(gunTurn);
        if (getGunHeat() == 0 && gunTurn < Math.atan2(9, target.getDistance())) {
        	setFire(power);
        }
	}
	
	private double getHeadOnTargetTurn(RobotModel target) {
		return calc.headOnTargetTurn(target.getBearingRadians(), getHeadingRadians(), getRadarHeadingRadians());
	}

	@Override
	public void onRobotDeath(RobotDeathEvent e) {
		removeRobotFromMap(e.getName());
	}

	@Override
	public void onScannedRobot(ScannedRobotEvent e) {
		registerScannedRobot(e);
	}

	private void removeRobotFromMap(String name) {
		for (RobotModel scanned : radarMap.values()) {
			if (scanned.getName().equals(name)) {
				radarMap.remove(scanned.getName());
			}
		}
	}

	private void registerScannedRobot(ScannedRobotEvent event) {
		RobotModel robot = radarMap.get(event.getName());
		if (robot == null) {
			robot = new RobotModel();
			robot.setName(event.getName());
		}
		robot.setBearing(event.getBearing());
		robot.setBearingRadians(event.getBearingRadians());
		robot.setDistance(event.getDistance());
		robot.setEnergy(event.getEnergy());
		robot.setHeading(event.getHeading());
		robot.setHeadingRadians(event.getHeadingRadians());
		robot.setVelocity(event.getVelocity());
		robot.setLastTurnScanned(turn);
		radarMap.put(robot.getName(), robot);
		if (!targetStatsMap.containsKey(robot.name)) {
			int[] stats = new int[31];
			targetStatsMap.put(robot.name, stats);
		}
		lookForTarget = !attack();
	}

}
